跳至主要内容

Module 7:Revert、Reset 與 Rebase

這三組指令都會影響版本,但目的與風險完全不同:

指令主要用途會重寫既有歷史嗎?初學者原則
git revert用新 commit 抵消舊 commit不會已 push 的共享歷史優先用它
git reset移動目前分支指標只處理尚未分享的本地 commit
git rebase把 commit 重新接到新基底只整理自己可控制的分支
請使用練習 Repository

不要第一次就在重要專案練習 reset --hard 或 rebase。以下步驟會建立一個可隨時丟棄的本地 repository。

Step 1:建立安全練習環境

cd ~/git-projects
mkdir git-history-lab
cd git-history-lab
git init -b main

echo "line 1" > story.txt
git add story.txt
git commit -m "docs: add line 1"

echo "line 2" >> story.txt
git add story.txt
git commit -m "docs: add line 2"

echo "line 3" >> story.txt
git add story.txt
git commit -m "docs: add line 3"

查看三個版本:

git log --oneline --decorate

Revert:用新版本安全抵消修改

要取消最後一個 commit,但保留完整紀錄:

git revert --no-edit HEAD
git log --oneline --decorate
cat story.txt

你會看到:

  • 歷史中新增一個 Revert ... commit。
  • 原本加入 line 3 的 commit 仍然存在。
  • 最新檔案狀態不再包含 line 3。

要 revert 特定版本,先用 git log --oneline 找編號:

git revert <commit-id>

如果發生衝突,處理方式和 merge 類似:編輯檔案、git add,再執行 git revert --continue。想取消則使用:

git revert --abort

Reset:移動分支指標

Reset 常用來拆掉尚未 push 的本地 commit。三種模式的差別是「commit 拆掉後,檔案修改放在哪裡」。

模式Commit暫存區工作目錄
--soft拆掉保留修改保留修改
--mixed拆掉取消暫存保留修改
--hard拆掉清除清除

--soft:想重新整理 Commit

先建立一個測試 commit:

echo "draft" > draft.txt
git add draft.txt
git commit -m "wip"
git reset --soft HEAD~1
git status

draft.txt 的修改仍在暫存區,可直接用更好的訊息重新 commit:

git commit -m "docs: add draft"

--mixed:想重新選擇要 Add 的檔案

--mixed 是未指定模式時的預設值:

git reset HEAD~1
git status

上一個 commit 被拆掉,修改留在工作目錄但不在暫存區。可重新檢查與分批加入:

git add draft.txt
git commit -m "docs: add reviewed draft"

--hard:連未提交內容一起清除

先建立救援分支,再練習:

git branch rescue-before-hard
git reset --hard HEAD~1
git log --oneline --decorate --all

--hard 會讓暫存區與工作目錄都符合目標 commit,未保存的修改可能無法找回。這次因為先建立救援分支,可復原:

git reset --hard rescue-before-hard

Reflog:尋找分支曾經指向的位置

如果 reset 或切換後找不到剛才的 commit,可先查看本地操作紀錄:

git reflog

找到目標 commit 後,先建立救援分支最安全:

git switch -c recovery <commit-id>

Reflog 只存在本機,而且紀錄會過期;它是救援工具,不是備份策略。

Rebase:把 Commit 重新接到新的基底

假設你從較舊的 main 開始開發,後來 main 已有新 commit。Rebase 會把功能分支的 commit 暫時取下,再依序接到最新 main 後面。

注意 F1'F2' 是重新產生的新 commit,所以編號會改變。

在功能分支執行:

git switch feature/my-change
git fetch origin
git rebase origin/main

若沒有衝突,功能分支會移到最新 origin/main 後方。若發生衝突:

# 編輯衝突檔案後
git add <resolved-files>
git rebase --continue

每遇到一個衝突就重複處理。想完全取消這次 rebase:

git rebase --abort

Rebase 的安全規則

  • 可以 rebase:尚未分享、只有自己使用的功能分支。
  • 不要任意 rebase:團隊共同使用或已被其他人拉取的分支。
  • Rebase 後若分支先前已 push,更新遠端可能需要 force push;先依團隊規則處理。
  • 確定必須更新自己的遠端分支時,--force-with-lease--force 多一層他人更新檢查,但仍應先確認分支無人共用。
git push --force-with-lease

怎麼選?

不確定時,先停止操作並保存 git statusgit log --oneline --graph --allgit reflog 的結果,再決定下一步。

官方文件